package com.spring.beanpostprocessor;


import com.spring.annotation.*;
import com.spring.bean.BeanDefinition;
import com.spring.context.BeanDefinitionRegistry;
import com.spring.enums.ScopeType;
import com.spring.reader.ComponentScanAnnotationParser;
import com.spring.util.StringUtils;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Method;
import java.util.*;

/**
 * 用来真正解析 @Configuration 类的对象
 *
 * 这里它直接获取 bean 定义信息， 因为初始状态只有 @Configuration  的 beanDefinition 被放入进来了
 */
@Slf4j
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor {
	
	private final BeanDefinitionRegistry registry;
	
	private final ComponentScanAnnotationParser componentScanAnnotationParser;
	
	
	public ConfigurationClassPostProcessor(BeanDefinitionRegistry registry) {
		this.registry = registry;
		this.componentScanAnnotationParser = new ComponentScanAnnotationParser();
	}
	
	
	/**
	 * 从注册器中派生更多的类定义
	 *
	 * 简单说就是，查找现有注册器中所有的类定义，查找 有  @Configuration 的类定义，并进一步加载 @ComponentScan 类
	 */
	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
		processConfigBeanDefinitions(registry);
	}
	
	
	/**
	 * 处理 配置类定义
	 */
	private void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		// 从容器中获取所有 配置类的 定义并处理 ，会过滤已解析的类
		registry.getAllBeanDefinition()
				.stream()
				.filter(bd -> bd.getBeanClass().isAnnotationPresent(Configuration.class))
				.forEach(this::doProcessConfigurationClass);
	}
	
	
	/**
	 * 实际处理配置类的方法
	 *
	 * 当前仅对类上的 @ComponentScan 和  方法上的@Bean 处理
	 */
	private void doProcessConfigurationClass(BeanDefinition bd) {
		// 已解析则不再重复解析
		if (bd.isResolved()) {
			return;
		}
		// 先标记这个类已经解析过了，后续不要重复解析
		bd.setResolved(true);
		
		
		Class<?> clazz = bd.getBeanClass();
		
		List<BeanDefinition> bds = new ArrayList<>(16);
		
		// 处理 @PropertySource 注解
		// 这部分暂不实现
		
		
		// 处理 @ComponentScan
		// 作为一个常规 bean 定义放入容器，等待后续 bean 注册器处理了
		if (clazz.isAnnotationPresent(ComponentScan.class)) {
			List<BeanDefinition> beanDefinitions = componentScanAnnotationParser.parse(clazz);
			bds.addAll(beanDefinitions);
		}
		
		// 处理@Import注解
		// 暂不实现
		
		
		// 处理 @Bean 部分
		Method[] methods = clazz.getMethods();
		for (Method method : methods) {
			processBeanMethod(bds, bd, method);
		}
		
		
		// 注册一下未注册过的bean定义
		for (BeanDefinition beanDefinition : bds) {
			if (!registry.containsBeanDefinition(beanDefinition.getBeanName())) {
				registry.registryBeanDefinition(beanDefinition.getBeanName(), beanDefinition);
			}
		}
	}
	
	
	/**
	 * 处理 带有 @Bean 的方法，添加 bean 定义
	 *
	 * 这里的bean 定义 没有被代理，也就是说 @Bean 方法被用户多次调用，会返回多个对象
	 * todo  以后有空再实现代理，让其始终返回一个对象
	 */
	private void processBeanMethod(List<BeanDefinition> bds, BeanDefinition bd, Method method) {
		Bean annotation = method.getAnnotation(Bean.class);
		if (annotation == null) {
			return;
		}
		
		// 返回空的方法无视掉 , 有参数的方法也无视掉
		if (Void.class.equals(method.getReturnType()) || method.getParameterCount() != 0) {
			log.warn("@Bean注解被错误配置在 {} 的空方法 {} 上", bd.getBeanClass().getName(), method.getName());
			return;
		}
		
		
		BeanDefinition mbd = new BeanDefinition();
		mbd.setBeanName(StringUtils.isNotEmpty(annotation.name()) ? annotation.name() : method.getName());
		mbd.setBeanClass(method.getReturnType());
		mbd.setFactoryBeanName(bd.getBeanName());
		mbd.setFactoryMethodName(method.getName());
		mbd.setScope(ScopeType.SINGLE);
		
		// todo  参数类型如何处理 , 暂时不处理 ，无参工厂方法
		//mbd.setParameterTypes(method.getParameterTypes());
		
		mbd.setInitMethodName(annotation.initMethod());
		mbd.setDestroyMethodName(annotation.destroyMethod());
		
		
		bds.add(mbd);
	}
	
	
}
